/*
 * Copyright (c) 2001-2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Ron Dreslinski
 */

/* @file
 */

#ifndef __PHYSICAL_MEMORY_HH__
#define __PHYSICAL_MEMORY_HH__

#include <map>
#include <string>

#include "base/range.hh"
#include "base/statistics.hh"
#include "mem/mem_object.hh"
#include "mem/packet.hh"
#include "mem/tport.hh"
#include "params/PhysicalMemory.hh"
#include "sim/eventq.hh"
#include "sim/stats.hh"

//for dramsim
#undef DEBUG
#include "./DRAMSim2/MultiChannelMemorySystem.h"

#define BLP_QUANTUM 100  // 10 dram cycles

class MemProgressEvent : public Event
{
  protected:
    Tick _interval;
    PhysicalMemory* mem;
    bool _repeatEvent;

  public:
    MemProgressEvent(PhysicalMemory*_cpu, Tick ival = 0);

    void process();

    void repeatEvent(bool repeat) { _repeatEvent = repeat; }
//    virtual const char *description() const;
};
//
// Functional model for a contiguous block of physical memory. (i.e. RAM)
//
class PhysicalMemory : public MemObject
{
  protected:

    class MemoryPort : public Port
    {
        PhysicalMemory *memory;

      public:

        MemoryPort(const std::string &_name, PhysicalMemory *_memory)
            : Port(_name, _memory), memory(_memory)
        { }


      protected:
        virtual bool recvTiming( PacketPtr pkt)
        {
            if ( pkt->memInhibitAsserted() )
            {
                warn(" dramport recvTiming memInhibitAsserted addr:%llx", pkt->getAddr());
                delete pkt;
                return true;
            }
            return memory-> recvTiming(pkt);
        }

        virtual Tick recvAtomic(PacketPtr pkt) 
        {
            memory->doAtomicAccess(pkt);
            return Tick(1);
        }

        virtual void recvFunctional(PacketPtr pkt) {
            memory->doFunctionalAccess( pkt );
        }

        virtual void recvStatusChange(Status status) { 
            memory->recvStatusChange( status );
        }

        virtual void recvRetry( ) { 
            memory->recvRetry( ); 
        }

        virtual void getDeviceAddressRanges(AddrRangeList &resp, bool &snoop) {
            memory->getAddressRanges( resp, snoop);
        }

    };

    int numPorts;


  private:
    // prevent copying of a MainMemory object
    PhysicalMemory(const PhysicalMemory &specmem);
    const PhysicalMemory &operator=(const PhysicalMemory &specmem);

  protected:

    uint8_t *pmemAddr;
    Tick lat;
    Tick lat_var;
    std::vector<MemoryPort*> ports;
    typedef std::vector<MemoryPort*>::iterator PortIterator;

    uint64_t _size;
    uint64_t _start;
    int tx_queue_size;
    int max_outstanding;

  public:
    uint64_t dram_clk;
  protected:

    /** Number of total bytes read from this memory */
    Stats::Scalar bytesRead;
    /** Number of instruction bytes read from this memory */
    Stats::Scalar bytesInstRead;
    /** Number of bytes written to this memory */
    Stats::Scalar bytesWritten;
    /** Number of read requests */
    Stats::Scalar numReads;
    /** Number of write requests */
    Stats::Scalar numWrites;
    /** Number of other requests */
    Stats::Scalar numOther;
    /** Read bandwidth from this memory */
    Stats::Formula bwRead;
    /** Read bandwidth from this memory */
    Stats::Formula bwInstRead;
    /** Write bandwidth from this memory */
    Stats::Formula bwWrite;
    /** Total bandwidth from this memory */
    Stats::Formula bwTotal;
    /** Total Deviation in miss requests */
    Stats::Distribution rt_lat;
    /** Standard deviation */
    Stats::Scalar latencySample;

    vector< vector < vector<int> > > blp;

  public:
    uint64_t size() { return _size; }
    uint64_t start() { return _start; }

  public:
    typedef PhysicalMemoryParams Params;
    PhysicalMemory(const Params *p);
    virtual ~PhysicalMemory();

    const Params *
        params() const
        {
            return dynamic_cast<const Params *>(_params);
        }

  public:
    unsigned deviceBlockSize() const;
    void getAddressRanges(AddrRangeList &resp, bool &snoop);
    virtual Port *getPort(const std::string &if_name, int idx = -1);
    void virtual init();
    void init_dramsim();
    unsigned int drain(Event *de);
    void clock(void);

  protected:
    void doAtomicAccess(PacketPtr pkt);
    void doFunctionalAccess(PacketPtr pkt);
    virtual Tick calculateLatency( void );
    void recvStatusChange(Port::Status status);
    bool recvTiming(Packet* pkt);

    // for dramsim
    void recvRetry();
    void sendTry();

    void blp_update(unsigned int threadId, uint64_t rank, uint64_t bank);
    void read_complete(unsigned int id, uint64_t addr, uint64_t clockcycle);
    void write_complete(unsigned int id, uint64_t addr, uint64_t clockcycle);

    bool waitRetry;
    std::deque<PacketPtr> recvQ;
    std::deque<PacketPtr> readyQ;
    std::deque<std::pair<PacketPtr,uint64_t> > readQ;
    std::deque<std::pair<PacketPtr,uint64_t> > writeQ;

    public:
    Tick fastfwd;
    string ddrConfigStr;
    uint64_t megsMem;

    // define this as void so we don't have to pull in DRAMSim header files
    DRAMSim::MultiChannelMemorySystem*  m_memorySystem;

  public:
    /**
     * Register Statistics
     */
    void regStats();

    virtual void serialize(std::ostream &os);
    virtual void unserialize(Checkpoint *cp, const std::string &section);

};

#endif //__PHYSICAL_MEMORY_HH__
